#pragma once

#include<vector>
#include <string>
#include "opencv2/core/core.hpp"

#include "json.hpp"
#include "sdc.h"
#include "cache.hpp"
#include "lru_cache_policy.hpp"
#include <SQLiteCpp/SQLiteCpp.h>
#include <SQLiteCpp/VariadicBind.h>

#include "hw_app_log.h"
#include "hw_cv_type.h"
#include "types.h"

extern GC_APP_CONFIG gc_app_config;
extern SQLite::Database    gc_db;
extern std::string app_start_time;
extern std::mutex last_track_data_mutex;
extern GCTrackData last_track_data;
extern float m_mapRatio;
// extern caches::fixed_sized_cache<const char*, cv::Mat, caches::LRUCachePolicy> IMG_CACHE;

using namespace std;

namespace gc
{
    /**
     * @brief 
     * 
     * @param atlbr 
     * @param btlbr 
     * @param union_type 0 atlbr 作为union area; 1 btlbr 作为union area; 2 标准union
     * @return float 
     */
    static inline float iou(const vector<int> &atlbr, const vector<int> &btlbr, int union_type)
    {
		int box_area = (btlbr[2] - btlbr[0] + 1)*(btlbr[3] - btlbr[1] + 1);
        int iw = min(atlbr[2], btlbr[2]) - max(atlbr[0], btlbr[0]) + 1;
        if (iw > 0)
        {
            int ih = min(atlbr[3], btlbr[3]) - max(atlbr[1], btlbr[1]) + 1;
            if(ih > 0)
            {
                int ua = 0;
                if (union_type == 0){
                    ua = (atlbr[2] - atlbr[0] + 1)*(atlbr[3] - atlbr[1] + 1);
                } else if (union_type == 1) {
                    ua = box_area;
                } else {
                    ua = (atlbr[2] - atlbr[0] + 1)*(atlbr[3] - atlbr[1] + 1) + box_area - iw * ih;
                }
                return iw * ih * 1.f / ua * 1.f;
            }
            else
            {
                return 0.0;
            }
        }
        else
        {
            return 0.0;
        }
    }

    /**
     * @brief 
     * 
     * @param p1 
     * @param p2 
     * @return float 
     */
    static inline float distance(const vector<int> &p1, const vector<int> &p2)
    {
        if (p1.size() < 2 || p2.size() < 2) {
            return 0.f;
        }
        return std::sqrt(std::pow(p1[0] - p2[0], 2) + std::pow(p1[1] - p2[1], 2));
    }

    typedef struct GCRect {
        int x;        // 矩形左上顶点的x坐标
        int y;        // 矩形左上顶点的y坐标
        int width;    // 矩形宽
        int height;   // 矩形高
        LandmarkInfo landmark;
        float fScore;
        int enClass;

        GCRect() {}

        GCRect(int x0, int y0, int width0, int height0, LandmarkInfo landmark0, float fScore0, int enClass0) {
            x = x0;
            y = y0;
            width = width0;
            height = height0;
            landmark = landmark0;
            fScore = fScore0;
            enClass = enClass0;
        }
    } GCRect;

    typedef struct GCTrackObj {
        long pts;
        char time_str[30];
        GCRect rect; // top left width height
        vector<int> cxby; // center x, bottom y 底部中心点，斜照时x取靠近车位方向的1/4处
        vector<int> tlbr; // top left, bottom right
        vector<int> trbl; // top right, bottom left
        vector<int> polygon;
        int track_id;
        int state;
        float score;
        char img_path[100];
        nlohmann::json lpr_result; // 目标检测框车牌识别结果
        nlohmann::json frame_lpr_result; // 整帧图车牌识别结果
        bool is_cross_frame; // 是否为跨线后的一帧
        bool is_proof;
        int frame_obj_count; // 当前帧包含追踪目标数量
        vector<GCRect> car_boxes; // 当前帧所有车辆检测框
        vector<GCRect> plate_boxes; // 当前帧所有车牌检测框
        vector<GCRect> car_plate_boxes; // 当前帧车辆包含的所有车牌检测框
        std::string camera_key; // 追踪目标所在相机key
    } GCTrackObj;

    typedef enum InoutType {
        INIT = -1,
        IN = 1,
        OUT = 0,
    } InoutType;

    typedef struct GCTrackPlate {
        GCTrackObj* track_obj;
        nlohmann::json plate_data;
        GCRect rect; // top left width height
        vector<int> cxby; // center x, bottom y
        vector<int> tlbr; // top left, bottom right
    } GCTrackPlate;

    typedef struct GCTrackResult {
        std::string camera_key;
        int track_id;
        long pts;
        char time_str[30];
        string plate;
        string plate_color;
        InoutType inout;
        double inout_conf;
        double plate_conf;
        vector<GCTrackObj> proof_track_objs;
        vector<GCTrackObj> state_track_objs;
        cv::Mat plate_img;
        std::string berth;
        double berth_conf;
        std::string id;
    } GCTrackResult;

    typedef enum DropReason {
        DR_INIT = -1,
        TOO_BIG = 0,
        TOO_SMALL = 1,
        TOO_BIG_WITH_IOU = 2,
        TOO_SMALL_WITH_IOU = 3,
    } DropReason;

    class InoutTrack
    {
        public:
        	InoutTrack(std::string camera_key, int track_id);
	        virtual ~InoutTrack(void);

            virtual bool AddTrackObj(GCTrackObj &trackObj);
            virtual void update(bool hasNewTrackObj);
            int IsPointInPolygon(vector<int> point, vector<int> polygon);
            virtual void End();

        public:
            std::string m_camera_key;
            int m_track_id;
            vector<GCTrackObj> m_track_objs;
            std::time_t m_start_time; // 轨迹开始时间
            std::time_t m_last_track_obj_time; // 最新的追踪目标产生时间
            bool m_track_end;
            GCTrackResult m_track_result;
            bool m_dirty;

        protected:
            bool IsConvertx(int poi[2], int s_poi[2]);
            bool IsOnSegment(int poi[2], int s_poi[2], int e_poi[2]);
            bool IsRayIntersectsSegment(int poi[2], int s_poi[2], int e_poi[2]);

            virtual void getPlate();
            virtual void getInoutTime();
            virtual void getProofImgs();
            virtual void getStateImgs();
            virtual void getBerth();
            virtual void drawDebugInfo();

            inline void checkInoutDistance();
            virtual void saveDB();
            virtual void upload();

        protected:
            float epsilon = 1e-5;
            int m_start_pos_side;
            GCTrackObj m_1st_track_obj;
            int m_drop_track_obj_coutinuous_count; // 相同原因连续丢弃的目标数
            DropReason m_drop_reason; // 丢弃目标原因
    };
}